using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace Elementum
{
    public abstract class ParticleSystem:Actor
    {
        public const int AlphaBlendDrawOrder = 100;
        public const int AdditiveDrawOrder = 200;

        private Texture2D texture;

        private Vector2 origin;

        private int howManyEffects;

       protected  Particle[] particles;

       protected Queue<Particle> freeParticles;

        public int FreeParticleCount
        {
            get { return freeParticles.Count; }
        }

        protected int minNumParticles;
        protected int maxNumParticles;
        protected string textureFilename;
        protected float minInitialSpeed;
        protected float maxInitialSpeed;
        protected float minAcceleration;
        protected float maxAcceleration;
        protected float minRotationSpeed;
        protected float maxRotationSpeed;
        protected float minLifetime;
        protected float maxLifetime;
        protected float minScale;
        protected float maxScale;
        protected BlendState blendState;



        public ParticleSystem(Stage stage,int howManyEffects):base(stage.World,stage) {
            InitializeConstants();
            LoadContent();
            this.howManyEffects = howManyEffects;
            particles = new Particle[howManyEffects * maxNumParticles];
            freeParticles = new Queue<Particle>(howManyEffects * maxNumParticles);
            for (int i = 0; i < particles.Length; i++)
            {
                particles[i] = new Particle();
                freeParticles.Enqueue(particles[i]);
            }
        }

        protected abstract void InitializeConstants();

        protected void LoadContent()
        {
            // make sure sub classes properly set textureFilename.
            if (string.IsNullOrEmpty(textureFilename))
            {
                string message = "textureFilename wasn't set properly, so the " +
                    "particle system doesn't know what texture to load. Make " +
                    "sure your particle system's InitializeConstants function " +
                    "properly sets textureFilename.";
                throw new InvalidOperationException(message);
            }
            // load the texture....
            texture = ResourceManager.LoadTexture(textureFilename);

            // ... and calculate the center. this'll be used in the draw call, we
            // always want to rotate and scale around this point.
            origin.X = texture.Width / 2;
            origin.Y = texture.Height / 2;

           // base.LoadContent();
        }

        public void AddParticles(Vector2 where)
        {
            // the number of particles we want for this effect is a random number
            // somewhere between the two constants specified by the subclasses.
            int numParticles =
                GameClass.Random.Next(minNumParticles, maxNumParticles);

            // create that many particles, if you can.
            for (int i = 0; i < numParticles && freeParticles.Count > 0; i++)
            {
                // grab a particle from the freeParticles queue, and Initialize it.
                Particle p = freeParticles.Dequeue();
                InitializeParticle(p, where);
            }
        }

        protected virtual void InitializeParticle(Particle p, Vector2 where)
        {
            // first, call PickRandomDirection to figure out which way the particle
            // will be moving. velocity and acceleration's values will come from this.
            Vector2 direction = PickRandomDirection();

            // pick some random values for our particle
            float velocity =
                GameClass.RandomBetween(minInitialSpeed, maxInitialSpeed);
            float acceleration =
                GameClass.RandomBetween(minAcceleration, maxAcceleration);
            float lifetime =
                GameClass.RandomBetween(minLifetime, maxLifetime);
            float scale =
                GameClass.RandomBetween(minScale, maxScale);
            float rotationSpeed =
                GameClass.RandomBetween(minRotationSpeed, maxRotationSpeed);

            // then initialize it with those random values. initialize will save those,
            // and make sure it is marked as active.
            p.Initialize(
                where, velocity * direction, acceleration * direction,
                lifetime, scale, rotationSpeed);
        }

        protected virtual Vector2 PickRandomDirection()
        {
            float angle = GameClass.RandomBetween(0, MathHelper.TwoPi);
            return new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle));
        }



        public override void Update(int delta)
        {
            float dt = ((float)delta) / 1000;

            // go through all of the particles...
            foreach (Particle p in particles)
            {

                if (p.Active)
                {
                    // ... and if they're active, update them.
                    p.Update(dt);
                    // if that update finishes them, put them onto the free particles
                    // queue.
                    if (!p.Active)
                    {
                        freeParticles.Enqueue(p);
                    }
                }
            }
        }

        public override void OnDraw(SpriteBatch spriteBatch, Matrix transform)
        {
            foreach (Particle p in particles)
            {
                // skip inactive particles
                if (!p.Active)
                    continue;

                float normalizedLifetime = p.TimeSinceStart / p.Lifetime;
                float alpha = 4 * normalizedLifetime * (1 - normalizedLifetime);
                Color color = Color.White * alpha;

                float scale = p.Scale * (.75f + .25f * normalizedLifetime);

                spriteBatch.Draw(texture, p.Position, null, color,
                    p.Rotation, origin, scale, SpriteEffects.None, 0.0f);
            }
        }

    }
}
